實務上我們可能會遇到這樣的需求:
需要避免多個執行緒同時占用同一個資源,來防止一些bug產生。
這時候C#中的lock可以幫上很大的忙~
lock可以把一段程式碼鎖定,讓這一段程式同時只容許一個執行緒執行,
若後續有其他執行緒也要執行這段程式,就必須等到lock解除後才能進入。
下面寫一個簡單的小程式來模擬沒有使用lock時會出現的程式bug:
private string testStringc = "";
private static string testStringd = "";
[HttpGet]
public string TEST(string a)
{
try
{
testStringc = a;
testStringd = a;
Thread.Sleep(5000);
return testStringc + " , " + testStringd;
}
catch
{
return "ERROR!";
}
}
首先分別宣告一個普通的string testStringc,和一個靜態類別的string testStringd,
然後跑一個簡單的function TEST,
這個function將testStringc和testStringd的值都指定為變數a後,
等待5秒,再return testStringc和testStringd的值。
在只有單一個執行緒的情況下,當指定a的值為123時,
可以正常的跑出預期的結果123,123
:
但如果另開一個無痕瀏覽器,並在5秒內分別在兩個瀏覽器視窗執行TEST時,
(無痕瀏覽器這邊指定a的值為456)
我們有機會看到先執行的那一個執行緒出現下面的結果:
因為testStringd是靜態類別的關係,導致執行結果變成123,456
了。
若要避免這個問題,可以將lock加入,把程式碼改寫為:
private static object lockObject = new object();
private string testStringc = "";
private static string testStringd = "";
[HttpGet]
public string TEST(string a)
{
try
{
lock (lockObject)
{
testStringc = a;
testStringd = a;
Thread.Sleep(5000);
return testStringc + " , " + testStringd;
}
}
catch
{
return "ERROR!";
}
}
宣告一個靜態物件lockObject作為lock的目標,
在執行TEST時,lock住lockObject,直到執行完return後,lock才會解除,
這樣改寫後,當第二個執行緒要執行TEST時,會因為lock的緣故,
必須等到第一個執行緒完成return後,才能接續執行TEST,
就不會出現第一個執行緒的執行結果被覆蓋的問題了~
lock在使用上最需要注意的是:避免使用在共用的物件上,以防死鎖的狀況,
另外執行時間越短越好,因為過多的資源獨佔會造成效能方面的問題。
參考文章
https://learn.microsoft.com/zh-tw/dotnet/csharp/language-reference/statements/lock
https://dotblogs.com.tw/yc421206/2011/01/07/20624
以及公司前輩熱心分享